// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/json/json_value_converter.h"

#include <memory>
#include <string>
#include <vector>

#include "base/json/json_reader.h"
#include "base/strings/string_piece.h"
#include "base/values.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {
namespace {

    // Very simple messages.
    struct SimpleMessage {
        enum SimpleEnum {
            FOO,
            BAR,
        };
        int foo;
        std::string bar;
        bool baz;
        bool bstruct;
        SimpleEnum simple_enum;
        std::vector<std::unique_ptr<int>> ints;
        std::vector<std::unique_ptr<std::string>> string_values;
        SimpleMessage()
            : foo(0)
            , baz(false)
            , bstruct(false)
            , simple_enum(FOO)
        {
        }

        static bool ParseSimpleEnum(const StringPiece& value, SimpleEnum* field)
        {
            if (value == "foo") {
                *field = FOO;
                return true;
            } else if (value == "bar") {
                *field = BAR;
                return true;
            }
            return false;
        }

        static bool HasFieldPresent(const base::Value* value, bool* result)
        {
            *result = value != NULL;
            return true;
        }

        static bool GetValueString(const base::Value* value, std::string* result)
        {
            const base::DictionaryValue* dict = NULL;
            if (!value->GetAsDictionary(&dict))
                return false;

            if (!dict->GetString("val", result))
                return false;

            return true;
        }

        static void RegisterJSONConverter(
            base::JSONValueConverter<SimpleMessage>* converter)
        {
            converter->RegisterIntField("foo", &SimpleMessage::foo);
            converter->RegisterStringField("bar", &SimpleMessage::bar);
            converter->RegisterBoolField("baz", &SimpleMessage::baz);
            converter->RegisterCustomField<SimpleEnum>(
                "simple_enum", &SimpleMessage::simple_enum, &ParseSimpleEnum);
            converter->RegisterRepeatedInt("ints", &SimpleMessage::ints);
            converter->RegisterCustomValueField<bool>("bstruct",
                &SimpleMessage::bstruct,
                &HasFieldPresent);
            converter->RegisterRepeatedCustomValue<std::string>(
                "string_values",
                &SimpleMessage::string_values,
                &GetValueString);
        }
    };

    // For nested messages.
    struct NestedMessage {
        double foo;
        SimpleMessage child;
        std::vector<std::unique_ptr<SimpleMessage>> children;

        NestedMessage()
            : foo(0)
        {
        }

        static void RegisterJSONConverter(
            base::JSONValueConverter<NestedMessage>* converter)
        {
            converter->RegisterDoubleField("foo", &NestedMessage::foo);
            converter->RegisterNestedField("child", &NestedMessage::child);
            converter->RegisterRepeatedMessage("children", &NestedMessage::children);
        }
    };

} // namespace

TEST(JSONValueConverterTest, ParseSimpleMessage)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1,\n"
                               "  \"bar\": \"bar\",\n"
                               "  \"baz\": true,\n"
                               "  \"bstruct\": {},\n"
                               "  \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}],"
                               "  \"simple_enum\": \"foo\","
                               "  \"ints\": [1, 2]"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    SimpleMessage message;
    base::JSONValueConverter<SimpleMessage> converter;
    EXPECT_TRUE(converter.Convert(*value.get(), &message));

    EXPECT_EQ(1, message.foo);
    EXPECT_EQ("bar", message.bar);
    EXPECT_TRUE(message.baz);
    EXPECT_EQ(SimpleMessage::FOO, message.simple_enum);
    EXPECT_EQ(2, static_cast<int>(message.ints.size()));
    ASSERT_EQ(2U, message.string_values.size());
    EXPECT_EQ("value_1", *message.string_values[0]);
    EXPECT_EQ("value_2", *message.string_values[1]);
    EXPECT_EQ(1, *(message.ints[0]));
    EXPECT_EQ(2, *(message.ints[1]));
}

TEST(JSONValueConverterTest, ParseNestedMessage)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1.0,\n"
                               "  \"child\": {\n"
                               "    \"foo\": 1,\n"
                               "    \"bar\": \"bar\",\n"
                               "    \"bstruct\": {},\n"
                               "    \"string_values\": [{\"val\": \"value_1\"}, {\"val\": \"value_2\"}],"
                               "    \"baz\": true\n"
                               "  },\n"
                               "  \"children\": [{\n"
                               "    \"foo\": 2,\n"
                               "    \"bar\": \"foobar\",\n"
                               "    \"bstruct\": \"\",\n"
                               "    \"string_values\": [{\"val\": \"value_1\"}],"
                               "    \"baz\": true\n"
                               "  },\n"
                               "  {\n"
                               "    \"foo\": 3,\n"
                               "    \"bar\": \"barbaz\",\n"
                               "    \"baz\": false\n"
                               "  }]\n"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    NestedMessage message;
    base::JSONValueConverter<NestedMessage> converter;
    EXPECT_TRUE(converter.Convert(*value.get(), &message));

    EXPECT_EQ(1.0, message.foo);
    EXPECT_EQ(1, message.child.foo);
    EXPECT_EQ("bar", message.child.bar);
    EXPECT_TRUE(message.child.baz);
    EXPECT_TRUE(message.child.bstruct);
    ASSERT_EQ(2U, message.child.string_values.size());
    EXPECT_EQ("value_1", *message.child.string_values[0]);
    EXPECT_EQ("value_2", *message.child.string_values[1]);

    EXPECT_EQ(2, static_cast<int>(message.children.size()));
    const SimpleMessage* first_child = message.children[0].get();
    ASSERT_TRUE(first_child);
    EXPECT_EQ(2, first_child->foo);
    EXPECT_EQ("foobar", first_child->bar);
    EXPECT_TRUE(first_child->baz);
    EXPECT_TRUE(first_child->bstruct);
    ASSERT_EQ(1U, first_child->string_values.size());
    EXPECT_EQ("value_1", *first_child->string_values[0]);

    const SimpleMessage* second_child = message.children[1].get();
    ASSERT_TRUE(second_child);
    EXPECT_EQ(3, second_child->foo);
    EXPECT_EQ("barbaz", second_child->bar);
    EXPECT_FALSE(second_child->baz);
    EXPECT_FALSE(second_child->bstruct);
    EXPECT_EQ(0U, second_child->string_values.size());
}

TEST(JSONValueConverterTest, ParseFailures)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1,\n"
                               "  \"bar\": 2,\n" // "bar" is an integer here.
                               "  \"baz\": true,\n"
                               "  \"ints\": [1, 2]"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    SimpleMessage message;
    base::JSONValueConverter<SimpleMessage> converter;
    EXPECT_FALSE(converter.Convert(*value.get(), &message));
    // Do not check the values below.  |message| may be modified during
    // Convert() even it fails.
}

TEST(JSONValueConverterTest, ParseWithMissingFields)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1,\n"
                               "  \"baz\": true,\n"
                               "  \"ints\": [1, 2]"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    SimpleMessage message;
    base::JSONValueConverter<SimpleMessage> converter;
    // Convert() still succeeds even if the input doesn't have "bar" field.
    EXPECT_TRUE(converter.Convert(*value.get(), &message));

    EXPECT_EQ(1, message.foo);
    EXPECT_TRUE(message.baz);
    EXPECT_EQ(2, static_cast<int>(message.ints.size()));
    EXPECT_EQ(1, *(message.ints[0]));
    EXPECT_EQ(2, *(message.ints[1]));
}

TEST(JSONValueConverterTest, EnumParserFails)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1,\n"
                               "  \"bar\": \"bar\",\n"
                               "  \"baz\": true,\n"
                               "  \"simple_enum\": \"baz\","
                               "  \"ints\": [1, 2]"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    SimpleMessage message;
    base::JSONValueConverter<SimpleMessage> converter;
    EXPECT_FALSE(converter.Convert(*value.get(), &message));
    // No check the values as mentioned above.
}

TEST(JSONValueConverterTest, RepeatedValueErrorInTheMiddle)
{
    const char normal_data[] = "{\n"
                               "  \"foo\": 1,\n"
                               "  \"bar\": \"bar\",\n"
                               "  \"baz\": true,\n"
                               "  \"simple_enum\": \"baz\","
                               "  \"ints\": [1, false]"
                               "}\n";

    std::unique_ptr<Value> value = base::JSONReader::Read(normal_data);
    SimpleMessage message;
    base::JSONValueConverter<SimpleMessage> converter;
    EXPECT_FALSE(converter.Convert(*value.get(), &message));
    // No check the values as mentioned above.
}

} // namespace base
